using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace codebase
{
    public class WAVE
    {
        int index;
        int dataSize;
        int sampleRate;

        /// <summary>
        /// Loads a WAV file. ONLY PCM 8-bit Mono.
        /// </summary>
        /// <param name="wav">WAV file bytes.</param>
        public WAVE(byte[] wav)
        {
            // see https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

            index = 0;
            if (wav[index + 0] != 'R' || wav[index + 1] != 'I' || wav[index + 2] != 'F' || wav[index + 3] != 'F')
            {
                throw new Exception("File is not RIFF");
            }
            index += 4;

            // ChunkSize
            uint ChunkSize = Utility.ExtractValueFromArray(wav, index, 4);
            index += 4;

            //format
            if (wav[index + 0] != 'W' || wav[index + 1] != 'A' || wav[index + 2] != 'V' || wav[index + 3] != 'E')
            {
                throw new Exception("File is not WAVE format");
            }
            index += 4;
            ////////////////////////////////////

            // fmt sub chunk //////////////////////
            //subchunk ID
            if (wav[index + 0] != 'f' || wav[index + 1] != 'm' || wav[index + 2] != 't' || wav[index + 3] != ' ')
            {
                throw new Exception("Unexpected fmt subchunk!");
            }
            index += 4;

            bool BitVarSampleRate16;

            uint Subchunk1Size = Utility.ExtractValueFromArray(wav, index, 4);
            index += 4;
            if (Subchunk1Size == 16)
            {
                BitVarSampleRate16 = true;
            }
            else if (Subchunk1Size == 18)
            {
                BitVarSampleRate16 = false;
            }
            else
            {
                throw new Exception("Invalid Subchunk1Size.");
            }

            ushort AudioFormat = (ushort)Utility.ExtractValueFromArray(wav, index, 2);
            index += 2;
            if (AudioFormat != 1)
            {
                throw new Exception("AudioFormat invalid.");
            }

            ushort NumChannels = (ushort)Utility.ExtractValueFromArray(wav, index, 2);
            index += 2;
            if (NumChannels != 1)
            {
                throw new Exception("Must be mono.");
            }

            sampleRate = (int)Utility.ExtractValueFromArray(wav, index, 4);
            index += 4;
            if (sampleRate != 8000)
            {
                throw new Exception("Sample rate must be 8000KHz.");
            }

            ushort ByteRate = (ushort)Utility.ExtractValueFromArray(wav, index, 4);
            index += 4;

            ushort BlockAlign = (ushort)Utility.ExtractValueFromArray(wav, index, 2);
            index += 2;

            if (BitVarSampleRate16)
            {
                ushort BitsPerSample = (ushort)Utility.ExtractValueFromArray(wav, index, 2);
                index += 2;
                if (BitsPerSample != 8)
                {
                    throw new Exception("Must be 8 bit.");
                }
            }
            else
            {
                uint BitsPerSample = Utility.ExtractValueFromArray(wav, index, 4);
                index += 4;
                if (BitsPerSample != 8)
                {
                    throw new Exception("Must be 8 bit.");
                }
            }

            ///////////////////////////////////////////

            //// data sub-chunk ///////////////////////////////////////
            if (wav[index + 0] != 'd' || wav[index + 1] != 'a' || wav[index + 2] != 't' || wav[index + 3] != 'a')
            {
                throw new Exception("Unexpected data subchunk!");
            }
            index += 4;

            uint Subchunk2Size = (ushort)Utility.ExtractValueFromArray(wav, index, 4);
            index += 4;

            dataSize = (int)Subchunk2Size;
            ////////////////////////////////////////////
        }

        public int GetDataIndex()
        {
            return index;
        }

        public int GetDataSize()
        {
            return dataSize;
        }

        public int GetSampleRate()
        {
            return sampleRate;
        }
    }
}
